package com.easyoffer.common.util;
import lombok.Data;
import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.IntegerCodec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * 通用计数类(基于redis)
 */
@Component
@Data
public class CountUtils {

    @Autowired
    RedissonClient redissonClient;

    /**
     * 增加并返回计数 默认统计一分钟内的计数
     * @param key 缓存键
     * @return 统计数
     */
    public long incrAndGetCounter(String key) {
        return incrAndGetCounter(key, 1, TimeUnit.MINUTES);
    }


    /**
     * 增加并返回计数
     * @param key 缓存键
     * @param timeInterval 统计窗口
     * @param timeUnit 时间单位
     * @return 返回当前统计窗口内的计数
     */
    public long incrAndGetCounter(String key, int timeInterval, TimeUnit timeUnit) {
        //以时间窗口和时间类型作为缓存键过期时间
        int expireTimeInseconds = 0 ;
        switch (timeUnit){
            case SECONDS:
                expireTimeInseconds = timeInterval;
                break;
            case MINUTES:
                expireTimeInseconds = timeInterval * 60;
                break;
            case HOURS:
                expireTimeInseconds = timeInterval * 3600;
                break;
            default:
                throw new IllegalArgumentException("TimeUnit is not supported. Use SECONDS, MINUTES, or HOURS.");
        }
        return incrAndGetCounter(key, timeInterval, timeUnit, expireTimeInseconds);
    }



    /**
     * 增加并返回计数
     * @param key 缓存键
     * @param timeInterval 统计窗口
     * @param timeUnit 时间单位
     * @param expirationTimeInSeconds 过期时间
     * @return 返回当前统计窗口内的计数
     */
    public long incrAndGetCounter(String key, int timeInterval, TimeUnit timeUnit, int expirationTimeInSeconds) {
        //当前计数处于的时间段
        long timeFrame;
        switch (timeUnit){
            case SECONDS:
                //获取以秒为基准的时间戳，与时间间隔求出当前时间段
                timeFrame = Instant.now().getEpochSecond() / timeInterval;
                break;
            case MINUTES:
                //获取以分钟为基准的时间戳，与时间间隔求出当前时间段
                timeFrame = Instant.now().getEpochSecond() / 60 / timeInterval;
                break;
            case HOURS:
                //获取以小时为基准的时间戳，与时间间隔求出当前时间段
                timeFrame = Instant.now().getEpochSecond() / 3600 / timeInterval;
                break;
            default:
                throw new IllegalArgumentException("TimeUnit is not supported. Use SECONDS, MINUTES, or HOURS.");
        }
        String countKey = key + ":" + timeFrame;
        // Lua脚本 对key的次数增加一次并设置过期时间
        String luaScript =
                "if redis.call('exists', KEYS[1]) == 1 then " +
                        "  return redis.call('incr', KEYS[1]); " +
                        "else " +
                        "  redis.call('set', KEYS[1], 1); " +
                        "  redis.call('expire', KEYS[1], ARGV[1]); " +
                        "  return 1; " +
                        "end";
        RScript rScript = redissonClient.getScript(IntegerCodec.INSTANCE);
        Object res =  rScript.eval(
                RScript.Mode.READ_WRITE,
                luaScript,
                RScript.ReturnType.INTEGER,
                Collections.singletonList(countKey), expirationTimeInSeconds);
        return (long) res;
    }
}
